// ========== Copyright Header Begin ==========================================
// 
// OpenSPARC T1 Processor File: Ni_Core.cc
// Copyright (c) 2006 Sun Microsystems, Inc.  All Rights Reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
// 
// The above named program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License version 2 as published by the Free Software Foundation.
// 
// The above named program is distributed in the hope that it will be 
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with this work; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
// 
// ========== Copyright Header End ============================================
/************************************************************************
**  
**  Copyright (C) 2002, Sun Microsystems, Inc.
**
**  Sun considers its source code as an unpublished, proprietary
**  trade secret and it is available only under strict license provisions.
**  This copyright notice is placed here only to protect Sun in the event 
**  the source is deemed a published work. Disassembly, decompilation,
**  or other means of reducing the object code to human readable form
**  is prohibited by the license agreement under which this code is
**  provided to the user or company in possession of this copy."
**
*************************************************************************/
#include "Ni/Ni_Cpu.h"
#include "Ni/Ni_Core.h"
#include "Ni/Ni_Strand.h"
#include "StdMacros.h"
#include "Blaze_CBInterface.h"
#include <sstream>
#include <assert.h>

using namespace std;
using namespace Riesling;

AddressSpace* ass;   

#ifdef SPU_N1

#include "Spu/spu_api.h"

using namespace Ni_Spu;

namespace Ni_Spu {

void yell( const char* msg )
{
  cerr << "N1 SPU :: " << msg << endl;
}

int mem_access_fcn(int cpu_id, uint64 addr, int size, ACCESS_MODE mode, uint8 *md, void *memop)
{
  assert(ass);
  MemoryTransaction mtv;
  uint64_t data;
  
  assert((size & 7) == 0); 
  mtv.size(8);
  
  switch (mode)
  {
    case MEM_READ:
      for (int i = 0; size; size -= 8, i += 8, addr += 8)
      {
        mtv.paddr(addr);
        mtv.access(MemoryTransaction::READ);
        ass->access(mtv);
        data = mtv.getData(0);
        md[i+0x0] = (data >> 56) & 0xff; 
        md[i+0x1] = (data >> 48) & 0xff; 
        md[i+0x2] = (data >> 40) & 0xff;
        md[i+0x3] = (data >> 32) & 0xff;
        md[i+0x4] = (data >> 24) & 0xff;
        md[i+0x5] = (data >> 16) & 0xff;
        md[i+0x6] = (data >>  8) & 0xff;
        md[i+0x7] = (data >>  0) & 0xff;
      }
      break;
    case MEM_WRITE:
      for (int i = 0; size; size -= 8, i += 8, addr += 8)
      {
        mtv.paddr(addr);
        mtv.access(MemoryTransaction::WRITE);
        data = ((uint64_t)md[i+0x0] << 56)
             | ((uint64_t)md[i+0x1] << 48)
             | ((uint64_t)md[i+0x2] << 40)
             | ((uint64_t)md[i+0x3] << 32)
             | ((uint64_t)md[i+0x4] << 24)
             | ((uint64_t)md[i+0x5] << 16)
             | ((uint64_t)md[i+0x6] <<  8)
             | ((uint64_t)md[i+0x7] <<  0);
        mtv.setData(0,data);
        ass->access(mtv);
      }
      break;
    case INTERNAL_WRITE:
      for (int i = 0; size; size -= 8, i += 8, addr += 8)
      {
        mtv.paddr(addr);
	if (BlazeCBInterface::cosim()){
            mtv.access(MemoryTransaction::WRITE |MemoryTransaction::INTERNAL); // Access_mode is INTERNAL_WRITE to indicate Streaming store for RTL Cosim
	    SimRegistry::ObjListT objs;
            int n = SimRegistry::getRootReg()->findAllChildren(objs,SimRegistry::CPU);
            Ni_Cpu* cpu = (Ni_Cpu*)objs.front();
            assert(cpu);
            Ni_Core* puc = (Ni_Core*)cpu->getCorePtr(cpu_id);
            Ni_Strand* ucp = (Ni_Strand*)puc->getStrandPtr(0x0);
	    mtv.setStrand(ucp);
        }
	else
	    mtv.access(MemoryTransaction::WRITE);
        data = ((uint64_t)md[i+0x0] << 56)
             | ((uint64_t)md[i+0x1] << 48)
             | ((uint64_t)md[i+0x2] << 40)
             | ((uint64_t)md[i+0x3] << 32)
             | ((uint64_t)md[i+0x4] << 24)
             | ((uint64_t)md[i+0x5] << 16)
             | ((uint64_t)md[i+0x6] <<  8)
             | ((uint64_t)md[i+0x7] <<  0);
        mtv.setData(0,data);
        ass->access(mtv);
      }
      break;

    default:
      assert(0);
  }
  return 0;
}

void raise_spu_trap( int thread_id, int cpu_id, int tt )
{
#ifdef RIESLING_NI_STANDANLONE
  SimRegistry::ObjListT objs;
  int n = SimRegistry::getRootReg()->findAllChildren(objs,SimRegistry::CPU);
  assert(n == 1); // > 1 not going to work with the spu ...
  Ni_Cpu* cpu = (Ni_Cpu*)objs.front();
  assert(cpu);
  Ni_Core* puc = (Ni_Core*)cpu->getCorePtr(cpu_id);
  Ni_Strand* ucp = (Ni_Strand*)puc->getStrandPtr(thread_id);
  ucp->getInstructionEmulator()->handleTrap(BasicTrap(tt));
#endif
}

}

#include "Spu/spu.h"
#include "Spu/spureg.h"

namespace Ni_Spu {
extern spu_obj_t* new_spu_obj( int cpu_id );
extern spu_obj_t* spu_obj_ptrs[MAX_CPU_CORES];
extern void reset_ctl_Go(int cpu_id);
extern SPU_SIM_MODE spu_sim_mode;
extern int (*mem_access_func)(int cpu_id, uint64 addr, int size, ACCESS_MODE mode, uint8 *md, void *memop);
extern void (*raise_spu_completion_trap)(int thread_id, int cpu_id, int tt);
}

#endif /* SPU_N1 */

Ni_Core::Ni_Core( Ni_Cpu& _cpu, uint_t core_id, uint_t max_core_id ) 
 :
  cpu_(_cpu),
  strand0_(*this,(4*core_id+0),(4*max_core_id)),
  strand1_(*this,(4*core_id+1),(4*max_core_id)),
  strand2_(*this,(4*core_id+2),(4*max_core_id)),
  strand3_(*this,(4*core_id+3),(4*max_core_id)),
  reg_("core"),
  itlb_( INSTRUCTION ),
  dtlb_( DATA ),
  coreId_(core_id),
  new_spu_op(false)   /* needed for RTL co-sim ,swerver-memory*/
{
  for (int k = 0; k < getNStrands(); k++) 
  {
    Ni_Strand* strand = dynamic_cast<Ni_Strand *>(getStrandPtr(k));
    strand->setTlb( &itlb_, &dtlb_);
    if( coreId_ == 0 ) {
      strand->getArchStatePtr()->getThreadStatusRegPtr()->setACTIVE(1);
      strand->sendResumeMessage();
    }
    else {
      strand->getArchStatePtr()->getThreadStatusRegPtr()->setACTIVE(0);
    }
    strand->getArchStatePtr()->getThreadStatusRegPtr()->setCPU_ID(coreId_);
    strand->getArchStatePtr()->getThreadStatusRegPtr()->setTHREAD_ID(k);
    strand->getAsiDispatcherPtr()->registerHandler( this );
  }
  
  // Well there is the SPU_init functiojn but it does things the wrong way around for us.
  // That SPU_init would best be called in Cpu but then it's too late! So hack our way around it
  // this way, after all it's noit that much to init.
 
#ifdef SPU_N1 
  new_spu_obj(coreId_);
  //spu_obj_ptrs[coreId_]->regfile=(uint8 *)malloc(SPU_REG_FILE_SIZE);
  spu_obj_ptrs[coreId_]->regfile=(uint8 *)calloc(SPU_REG_FILE_SIZE,1);
  reset_ctl_Go(coreId_);
  spu_sim_mode = SIMICS_FAST_MODE;
  mem_access_func = mem_access_fcn;
  
  raise_spu_completion_trap = raise_spu_trap;
#endif /* SPU_N1 */

}

Ni_Core::~Ni_Core()
{}

string Ni_Core::toString() const
{
    ostringstream os;

    os << strand0_.toString();
    os << "\n";
    os << strand1_.toString();
    os << "\n";
    os << strand2_.toString();
    os << "\n";
    os << strand3_.toString();

    return os.str();
}

void Ni_Core::step()
{
    strand0_.step();
    strand1_.step();
    strand2_.step();
    strand3_.step();
}

#ifdef SPU_N1

//spu_obj_t*
void *
Ni_Core::getSpuPtr() 
{
  return (spu_obj_ptrs[coreId_]); 
}

#endif /* SPU_N1 */

void Ni_Core::setAddrSpace( AddressSpace *addressSpace )
{
  strand0_.setAddrSpace( addressSpace );
  strand1_.setAddrSpace( addressSpace );
  strand2_.setAddrSpace( addressSpace );
  strand3_.setAddrSpace( addressSpace );
    
  ass = addressSpace;
}

uint_t Ni_Core::getNStrands() const
{
  return 4;
}

Ni_Tlb* Ni_Core::getiTlbPtr()
{
  return &itlb_ ;
}

Ni_Tlb* Ni_Core::getdTlbPtr()
{
  return &dtlb_ ;
}

Riesling::Strand* Ni_Core::getStrandPtr( uint_t strandNr )
{
  switch (strandNr)
  {
    case 0: return &strand0_;
    case 1: return &strand1_;
    case 2: return &strand2_;
    case 3: return &strand3_;
    default: assert(0);
  }
  return 0;
}

Ni_CoreConfigReg *Ni_Core::getCoreConfigRegPtr()
{
  return &coreConfig_;
}

void Ni_Core::setAsiDispatcher( AsiDispatcher &dispatcher )
{}

void Ni_Core::asiWriteHandler( AsiDispatchPacket &p )
{
  uint64_t data = p.getData();
  p.setResponse( AsiDispatchPacket::OK );

  switch (p.getAccessIndex())
  {
    case CORE_CONFIG_REG:
      coreConfig_.setNative( data );
      break;

    case INST_MASK:
      instMask_.setNative( data );
      break;

    case STM_CTL:
      stmCtl_.setNative( data );
      break;
  case STM_CTL_NOP:
      RIESLING_TRAP( Ni_Trap::treatAsNoop );
      break;

  case SPARC_BIST_CTL:
      sparcBistCtl_.setNative( data );
      break;

#ifdef SPU_N1
    case MA_CONTROL:
      data &= 0x3bff;
      SPU_write_reg(coreId_,REG_MA_CTL,0,8,(uint8*)&data);
       new_spu_op = true;
      break;
    case MA_MPA:
      data &= 0x7ffffffff8;
      SPU_write_reg(coreId_,REG_MPA,0,8,(uint8*)&data);
      break;
    case MA_ADDR:
      data &= 0xffffffffffff;
      SPU_write_reg(coreId_,REG_MA_ADDR,0,8,(uint8*)&data);
      break;
    case MA_NP:
      SPU_write_reg(coreId_,REG_NP,0,8,(uint8*)&data);
      break;
    case MA_SYNC:
      /* write to this register causes data access exception */
      //SPU_write_reg(coreId_,MASync,0,8,(uint8*)&data);
      //break;
#endif /* SPU_N1 */

    default:
      p.setResponse( AsiDispatchPacket::HANDLER_ERROR );
      break;
  }
}

void Ni_Core::asiReadHandler( AsiDispatchPacket &p )
{
  p.setResponse( AsiDispatchPacket::OK );
  uint64_t data = 0;
#ifdef SPU_N1
  Ni_Strand* strand = (Ni_Strand*)p.getInstructionWord()->getStrand();
  if(p.getAsi() == 0x40){
      /* if RTL indicates an aborted SPU transaction , treat as nop */
      if(strand->getSwerverMemSpuIllegalVa_()){
          /* reset flag */ 
          strand->setSwerverMemSpuIllegalVa_(false);
	  RIESLING_TRAP( Ni_Trap::treatAsNoop )
      }
  }
#endif /* SPU_N1 */
  switch (p.getAccessIndex()) 
  {
    case CORE_CONFIG_REG:
      data = coreConfig_.getNative();
      break;

    case INST_MASK:
      data = instMask_.getNative();
      break;

  case STM_CTL:
      data = stmCtl_.getNative();
      break;
  case STM_CTL_NOP:
      RIESLING_TRAP( Ni_Trap::treatAsNoop );
      break;

  case SPARC_BIST_CTL:
      data = sparcBistCtl_.getNative();
      break;

#ifdef SPU_N1
    case NOP_VA:
      RIESLING_TRAP( Ni_Trap::treatAsNoop );
    case MA_CONTROL:
      SPU_read_reg(coreId_,REG_MA_CTL,0,8,(uint8*)&data);
      break;
    case MA_MPA:
      SPU_read_reg(coreId_,REG_MPA,0,8,(uint8*)&data);
      break;
    case MA_ADDR:
      SPU_read_reg(coreId_,REG_MA_ADDR,0,8,(uint8*)&data);
      break;
    case MA_NP:
      SPU_read_reg(coreId_,REG_NP,0,8,(uint8*)&data);
      break;
    case MA_SYNC:
      SPU_read_reg(coreId_,MASync,0,8,(uint8*)&data);
      break;
#endif /* SPU_N1 */

    default:
      p.setResponse( AsiDispatchPacket::HANDLER_ERROR );
      break;
  }
  p.setData( data );
}

AsiDispatcher* Ni_Core::getAsiDispatcherPtr()
{
  assert(0);    // we don;t have one, there is one in every strand though ...
  return 0;
}

void Ni_Core::setParentRegistry( SimRegistry *parentPtr )
{
  // 1) Set the parent in my registry
  reg_.setParent( parentPtr );

  // 2) Register my components in the registry
  reg_.addInstance( "strand0", SimRegistry::STRAND, &strand0_ );
  reg_.addInstance( "strand1", SimRegistry::STRAND, &strand1_ );
  reg_.addInstance( "strand2", SimRegistry::STRAND, &strand2_ );
  reg_.addInstance( "strand3", SimRegistry::STRAND, &strand3_ );
    
  // 3) Set the parent in all of my childrent
  strand0_.setParentRegistry( &reg_ );
  strand1_.setParentRegistry( &reg_ );
  strand2_.setParentRegistry( &reg_ );
  strand3_.setParentRegistry( &reg_ );

  // 4) ONLY AFTER setting the parent of my children
  //    can I resolve ID in my subtree.
}

void Ni_Core::setThreadStatusSpeculativeEnable( bool v )
{
  strand0_.setThreadStatusSpeculativeEnable(v);
  strand1_.setThreadStatusSpeculativeEnable(v);
  strand2_.setThreadStatusSpeculativeEnable(v);
  strand3_.setThreadStatusSpeculativeEnable(v);
}


int
Ni_Core::hitInstrBpoint( InstructionWord &iw )
{
    if (((uint64_t)instMask_.getNative() >> 32) == 0) {
	// none of the fields is enabled, no hit
	return 0;
    }

    // at least one enabled field
    uint32_t instrPattern = instMask_.getINSTR();
    if (instMask_.getENB4_0() && (iw.rs2() != (instrPattern & 0x1f))) {
	// since we have to match ALL enabled fields, if any one of them
	// does not match, then there is no hit, no point to keep checking
	return 0;
    }
    if (instMask_.getENB12_5() && (iw.immAsi() != ((instrPattern >> 5) & 0xff))) {
	return 0;
    }
    if (instMask_.getENB13() && (iw.i() != ((instrPattern >> 13) & 0x1))) {
	return 0;
    }
    if (instMask_.getENB18_14() && (iw.rs1() != ((instrPattern >> 14) & 0x1f))) {
	return 0;
    }
    if (instMask_.getENB24_19() && (iw.op3() != ((instrPattern >> 19) & 0x3f))) {
	return 0;
    }
    if (instMask_.getENB29_25() && (iw.rd() != ((instrPattern >> 25) & 0x1f))) {
	return 0;
    }
    if (instMask_.getENB31_30() && (iw.op() != (instrPattern >> 30))) {
	return 0;
    }

    return 1;
}

bool 
Ni_Core::get_new_spu_op(){
    return new_spu_op;
}

void 
Ni_Core::reset_new_spu_op(){
    new_spu_op = false;
}

void 
Ni_Core::snapshot( SS_SnapShot &ss, const char *prefix )
{
    char extended_prefix[SS_SnapShot::LINE_SIZE];
	// Ni_Cpu&     cpu_;
	// int	    coreId_;
	// SimRegistry reg_;

	// Ni_InstMaskReg instMask_;
    sprintf(extended_prefix,"%s.instMask",prefix); instMask_.snapshot(ss, extended_prefix);
        
	// RIESLING_REGISTER_CHILD( itlb, Ni_Tlb, itlb_ );
	// RIESLING_REGISTER_CHILD( dtlb, Ni_Tlb, dtlb_ );
    sprintf(extended_prefix,"%s.itlb",prefix); itlb_.snapshot(ss, extended_prefix);
    sprintf(extended_prefix,"%s.dtlb",prefix); dtlb_.snapshot(ss, extended_prefix);

	// RIESLING_REGISTER_CHILD( strand0, Ni_Strand, strand0_ );
	// RIESLING_REGISTER_CHILD( strand1, Ni_Strand, strand1_ );
	// RIESLING_REGISTER_CHILD( strand2, Ni_Strand, strand2_ );
	// RIESLING_REGISTER_CHILD( strand3, Ni_Strand, strand3_ );
    sprintf(extended_prefix,"%s.strand0",prefix); strand0_.snapshot(ss, extended_prefix);
    sprintf(extended_prefix,"%s.strand1",prefix); strand1_.snapshot(ss, extended_prefix);
    sprintf(extended_prefix,"%s.strand2",prefix); strand2_.snapshot(ss, extended_prefix);
    sprintf(extended_prefix,"%s.strand3",prefix); strand3_.snapshot(ss, extended_prefix);

	// RIESLING_REGISTER_CHILD( core_config, Ni_CoreConfigReg, coreConfig_ );
	// Ni_StmCtlReg stmCtl_;
	// Ni_SparcBistCtlReg sparcBistCtl_;
    sprintf(extended_prefix,"%s.coreConfig",prefix); coreConfig_.snapshot(ss, extended_prefix);
    sprintf(extended_prefix,"%s.stmCtl",prefix); stmCtl_.snapshot(ss, extended_prefix);
    sprintf(extended_prefix,"%s.sparcBistCtl",prefix); sparcBistCtl_.snapshot(ss, extended_prefix);

	// /* needed for RTL co-sim ,swerver-memory*/
	// bool new_spu_op;
}
